unit QBIBWindow;

{$I QBuilderVerCtrl.inc}

interface

uses
  {$IFDEF VCL16}
    System.Classes,
    Vcl.Controls,
    Vcl.StdCtrls,
    Vcl.ComCtrls,
    Data.DB,
  {$ELSE}
    Classes,
    Controls,
    StdCtrls,
    ComCtrls,
    DB,
  {$ENDIF}
  QBWindow,
  QBParser,
  QBStringTypes;

const

  IBKeywords = 'ACTION,ACTIVE,ADD,ADMIN,AFTER,ALL,ALTER,AND,ANY,AS,ASC,' +
    'ASCENDING,AT,AUTO,AUTODDL,BASED,BASENAME,BASE_NAME,BEFORE,BEGIN,BETWEEN,BIGINT,BLOB,' +
    'BLOBEDIT,BOOLEAN,BREAK,BUFFER,BY,CACHE,CASCADE,CHAR,CHARACTER,CHARACTER_LENGTH,CHAR_LENGTH,' +
    'CHECK,CHECK_POINT_LEN,CHECK_POINT_LENGTH,COALESCE,COLLATE,COLLATION,COLUMN,' +
    'COMMIT,COMMITED,COMMITS,COMPILETIME,COMPUTED,CLOSE,CONDITIONAL,CONNECT,' +
    'CONSTRAINT,CONTAINING,CONTINUE,CREATE,CSTRING,CURRENT,CURRENT_CONNECTION,' +
    'CURRENT_DATE,CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP,' +
    'CURRENT_TRANSACTION,CURRENT_USER,CURSOR,DATABASE,DATE,DAY,DB_KEY,DEBUG,DEC,DECIMAL,' +
    'DECLARE,DEFAULT,DELETE,DELETING,DESC,DESCENDING,DESCRIBE,DESCRIPTOR,' +
    'DISCONNECT,DISPLAY,DISTINCT,DO,DOMAIN,DOUBLE,DROP,ECHO,EDIT,ELSE,END,ENTRY_POINT,' +
    'ESCAPE,EVENT,EXCEPTION,EXECUTE,EXISTS,EXIT,EXTERN,EXTERNAL,FALSE,FETCH,' +
    'FILE,FILTER,FIRST,FLOAT,FLUSH,FOR,FOREIGN,FOUND,FREE_IT,FROM,FULL,FUNCTION,' +
    'GDSCODE,GENERATOR,GLOBAL,GOTO,GRANT,GROUP,GROUP_COMMIT_WAIT,' +
    'GROUP_COMMIT_WAIT_TIME,HAVING,HELP,HOUR,IF,IMMEDIATE,IN,INACTIVE,INDEX,' +
    'INDICATOR,INIT,INNER,INPUT,INPUT_TYPE,INSERT,INSERTING,INT,INTEGER,INTO,IS,' +
    'ISOLATION,ISQL,JOIN,KEY,LC_MESSAGES,LC_TYPE,LEFT,LEAVE,LENGTH,LEV,LEVEL,' +
    'LIKE,LINGER,LOCK,LOGFILE,LOG_BUFFER_SIZE,LOG_BUF_SIZE,LONG,MANUAL,MAXIMUM,' +
    'MAXIMUM_SEGMENT,MAX_SEGMENT,MEMORY,MERGE,MESSAGE,MINIMUM,MINUTE,' +
    'MODULE_NAME,MONTH,NAMES,NATIONAL,NATURAL,NCHAR,NO,NOAUTO,NOT,NULL,NULLIF,' +
    'NUMERIC,NUM_LOG_BUFFS,NUM_LOG_BUFFERS,OCTET_LENGTH,OF,ON,ONLY,OPEN,OPTION,OR,' +
    'ORDER,OUTER,OUTPUT,OUTPUT_TYPE,OVERFLOW,PAGE,PAGELENGTH,PAGES,PAGE_SIZE,' +
    'PARAMETER,PASSWORD,PERCENT,PLAN,PLANONLY,POSITION,POST_EVENT,PRECISION,' +
    'PREPARE,PROCEDURE,PROTECTED,PRIMARY,PRIVILEGES,PUBLIC,QUIT,RAW_PARTITIONS,' +
    'READ,REAL,RECLAIM,RECORD_VERSION,RECREATE,REFERENCES,RELEASE,' +
    'RESERV,RESERVING,RESTRICT,RETAIN,RETURN,RETURNING_VALUES,RETURNS,REVOKE,' +
    'RIGHT,ROLE,ROW_COUNT,ROWS,ROLLBACK,RUNTIME,SAVEPOINT,SCHEMA,SECOND,' +
    'SEGMENT,SELECT,SET,SHADOW,SHARED,SHELL,SHOW,SINGULAR,SIZE,SKIP,SMALLINT,SNAPSHOT,' +
    'SOME,SORT,SQL,SQLCODE,SQLERROR,SQLWARNING,STABILITY,STARTING,STARTS,' +
    'STATEMENT,STATIC,STATISTICS,SUB_TYPE,SUSPEND,TABLE,TERMINATOR,THEN,TIES,TIME,TIMESTAMP,TO,' +
    'TRANSACTION,TRANSLATE,TRANSLATION,TRIGGER,TRIM,TRUE,TYPE,UNCOMMITTED,UNION,' +
    'UNIQUE,UNKNOWN,UPDATE,UPDATING,USER,USING,VALUE,VALUES,VARCHAR,VARIABLE,VARYING,' +
    'VERSION,VIEW,WAIT,WEEKDAY,WHEN,WHENEVER,WHERE,WHILE,WITH,WORK,WRITE,YEAR,' +
    'YEARDAY';

  IBFunctions = 'AVG,CAST,COUNT,EXTRACT,GEN_ID,MAX,MIN,SUBSTRING,SUM,UPPER,CURRENT_DATE';
  IBPredicates = 'EXISTS,SINGULAR';

  IBOperations = '+,-,*,/,||';

  IBBoolFalse = 'FALSE';
  IBBoolTrue = 'TRUE';

type

  TQBSQLDialect = (qibDialect1, qibDialect3);
  TQBIBSQLServer = (qssDontCare, qssInterBase, qssFireBird);

  TQBIBScanner = class(TQBSQLScanner)
  private
    FSQLDialect: TQBSQLDialect;
  protected
    function GetIdentQuote: aqbChar; override;
    function GetProcessAliases: boolean; override;
//    function IsAlpha(Ch: aqbChar): boolean; override;
    function IsQuote(Ch: aqbChar): boolean; override;
    function IsLegalSymbol(Ch: aqbChar): boolean; override;
    function GetCommaKeywords: aqbString; override;
    function GetCommaFunctions: aqbString; override;
    function GetCommaOperations: aqbString; override;
    function GetQuoteIdentifiers: boolean; override;
    function ProcessIdent(const S: aqbString): aqbString; override;
    procedure DefineBooleans; override;
    function GetCommaPredicates: aqbString; override;
    function GetFormatTokenIdent(const Ident: aqbString;
      const Quoted: boolean): aqbString; override;
  public
    constructor Create; override;
    function ProcessName(const S: aqbString): aqbString; override;
    function QuoteName(const S: aqbString): aqbString; override;
    function IsIdentQuote(Ch: aqbChar): boolean; override;
    function NeedQuotes(const Ident: aqbString): boolean; override;
    property SQLDialect: TQBSQLDialect read FSQLDialect write FSQLDialect;
  end;

  TQBFBOptions = class(TPersistent)
  private
    FFirst: integer;
    FSkip: integer;
    function GetScript: aqbString;
  public
    procedure Assign(Source: TPersistent); override;
    procedure Clear;
    property First: integer read FFirst write FFirst;
    property Skip: integer read FSkip write FSkip;
    property Script: aqbString read GetScript;
  end;

  TQBIBOptions = class(TPersistent)
  private
    FPercent: boolean;
    FTies: boolean;
    FRowsFrom: integer;
    FRowsBy: integer;
    FRowsTo: integer;
    FPlanTableName,
    FPlanName: aqbString;
    function GetScript: aqbString;
    function GetPlanScript: aqbString;
  public
    procedure Assign(Source: TPersistent); override;
    procedure Clear;
    property RowsFrom: integer read FRowsFrom write FRowsFrom;
    property RowsTo: integer read FRowsTo write FRowsTo;
    property RowsBy: integer read FRowsBy write FRowsBy;
    property Percent: boolean read FPercent write FPercent;
    property Ties: boolean read FTies write FTies;
    property Script: aqbString read GetScript;
    property PlanTableName: aqbString read FPlanTableName write FPlanTableName;
    property PlanName: aqbString read FPlanName write FPlanName;
    property PlanScript: aqbString read GetPlanScript;
  end;

  TQBIBSelectParser = class(TQBSelectParser)
  private
    FIBOptions: TQBIBOptions;
    FFBOptions: TQBFBOptions;
    FSQLServer: TQBIBSQLServer;
    procedure SetIBOptions(const Value: TQBIBOptions);
    procedure SetFBOptions(const Value: TQBFBOptions);
  protected
    function CreateScanner: TQBSQLScanner; override;
    function GetExpression: aqbString; override;
    procedure DefineOperators; override;
    procedure DoParseSelectClause; override;
    procedure DoParseAppendix; override;
    function GetCaseSensitiveIdentifiers: boolean; override;
    function NeedParentheses: boolean; override;
    procedure DoParsePlanClause; override;
  public
    constructor Create(AParent: TPersistent); override;
    destructor Destroy; override;
    procedure Clear; override;
    function GetParamAsString(AFieldType: TFieldType; AValue: Variant): aqbString; override;
    function IsProcEmptyInputParamsBracket: boolean; override;
    property IBOptions: TQBIBOptions read FIBOptions write SetIBOptions;
    property FBOptions: TQBFBOptions read FFBOptions write SetFBOptions;
    property SQLServer: TQBIBSQLServer read FSQLServer write FSQLServer;
  end;

  TQBOptionsPanel = class(TWinControl)
  private
    FParser: TQBIBSelectParser;
  public
    property Parser: TQBIBSelectParser read FParser write FParser;
  end;

  TQBFBOptionsPanel = class(TQBOptionsPanel)
  private
    FFirstEdit: TEdit;
    FSkipEdit: TEdit;
    procedure HandleFirstEditChange(Sender: TObject);
    procedure HandleSkipEditChange(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TQBIBOptionsPanel = class(TQBOptionsPanel)
  private
    FRowsFromEdit: TEdit;
    FRowsToEdit: TEdit;
    FRowsByEdit: TEdit;
    FPercentBox: TCheckBox;
    FTiesBox: TCheckBox;
    procedure HandleRowsFromEditChange(Sender: TObject);
    procedure HandleRowsToEditChange(Sender: TObject);
    procedure HandleRowsByEditChange(Sender: TObject);
    procedure HandlePercentBoxClick(Sender: TObject);
    procedure HandleTiesBoxClick(Sender: TObject);
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TIBQueryBuilder = class(TCustomQueryBuilder)
  protected
    procedure GetAvailableFields(Fields: TaqbStrings);override;
    procedure HandleChangeFieldAlias(const FieldName, Alias: aqbString;
      var NewAlias: aqbString);override;
  end;

  TInterBaseQueryBuilder = class(TFullQueryBuilder {TCustomQueryBuilder})
  private
    function GetSQLDialect: TQBSQLDialect;
    procedure SetSQLDialect(const Value: TQBSQLDialect);
    function GetSQLServer: TQBIBSQLServer;
    procedure SetSQLServer(const Value: TQBIBSQLServer);
  protected
    function CreateQueryBuilder(Owner: TComponent): TCustomQueryBuilder;override;
  public
    constructor Create(AOwner: TComponent); override;
    function CreateParser(AParent: TPersistent): TQBSelectParser; override;
  published
    property About;
    property Align;
    property Aggregates;
    property Functions;
    property SQLDialect: TQBSQLDialect read GetSQLDialect write SetSQLDialect
      default qibDialect3;
    property Keywords;
    property Operators;
    property Options;
    property Palette;
    property SQLServer: TQBIBSQLServer read GetSQLServer write SetSQLServer
      default qssDontCare;
    property ScriptFormat;
    property Storage;
    property Style;
    property TabHeight;
    property VisibleTabs;
    property _Version;
    // ayz
    property WorkAreaPopupMenu;
    property TabsPopupMenu;
    // \ayz

    property OnStartDrag;
    property OnDragOver;
    property OnDragDrop;
    property OnEndDrag;
    {$IFNDEF EC_UNICODE}
    property OnGetTableFields;
    property OnParserError;
    {$ELSE}
    property OnGetTableFieldsW;
    property OnParserErrorW;
    {$ENDIF}
    property OnAddRemoveTable;
    property OnAddRemoveLink;
    property OnEnterProcParameter;
    property OnCheckProcedureName;
  end;

implementation

uses 
  {$IFDEF VCL16}
	  System.SysUtils,
    System.StrUtils,
    System.Variants,
  {$ELSE}
	  SysUtils,
    {$IFDEF VCL6}
      StrUtils,
      Variants,
    {$ENDIF}
  {$ENDIF}
  {$IFDEF EC_UNICODE}
    ecStrUtils,
  {$ENDIF}
  QBResource,
  QbFloatTable;

{ TInterBaseQueryBuilder }

constructor TInterBaseQueryBuilder.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

function TInterBaseQueryBuilder.CreateParser(AParent: TPersistent): TQBSelectParser;
begin
  Result := TQBIBSelectParser.Create(AParent);
end;

function TInterBaseQueryBuilder.CreateQueryBuilder(
  Owner: TComponent): TCustomQueryBuilder;
begin
  Result :=   TIBQueryBuilder.Create(Owner);
end;

function TInterBaseQueryBuilder.GetSQLDialect: TQBSQLDialect;
begin
  if Parser.Scanner is TQBIBScanner then
    Result := TQBIBScanner(Parser.Scanner).SQLDialect
  else
    Result := Low(TQBSQLDialect);
end;

function TInterBaseQueryBuilder.GetSQLServer: TQBIBSQLServer;
begin
  Result := TQBIBSelectParser(Parser).SQLServer;
end;

procedure TInterBaseQueryBuilder.SetSQLDialect(const Value: TQBSQLDialect);
var I: Integer;
begin
  if Parser.Scanner is TQBIBScanner then
    TQBIBScanner(Parser.Scanner).SQLDialect := Value;
  for I := 0 to QueryBuilders.Count - 1 do
    TQBIBScanner(QueryBuilders[i].Data.Parser.Scanner).SQLDialect := Value;

end;

procedure TInterBaseQueryBuilder.SetSQLServer(
  const Value: TQBIBSQLServer);
begin
  if TQBIBSelectParser(Parser).SQLServer <> Value then
  begin
    TQBIBSelectParser(Parser).SQLServer := Value;
  end;
end;

{ TQBIBScanner }

constructor TQBIBScanner.Create;
begin
  inherited Create();
  FSQLDialect := qibDialect3;
end;

procedure TQBIBScanner.DefineBooleans;
begin
  Booleans[False] := IBBoolFalse;
  Booleans[True] := IBBoolTrue;
end;

function TQBIBScanner.GetCommaFunctions: aqbString;
begin
  Result := IBFunctions;
end;

function TQBIBScanner.GetCommaKeywords: aqbString;
begin
  Result := IBKeywords;
end;

function TQBIBScanner.GetCommaOperations: aqbString;
begin
  Result := IBOperations;
end;

function TQBIBScanner.GetCommaPredicates: aqbString;
begin
  Result := IBPredicates;
end;

function TQBIBScanner.GetFormatTokenIdent(const Ident: aqbString;
  const Quoted: boolean): aqbString;
begin
  Result := inherited GetFormatTokenIdent(Ident, Quoted);
  if not Quoted then
    Result := QBUpperCase(Ident);
end;

function TQBIBScanner.GetIdentQuote: aqbChar;
begin
  Result := '"';
end;

function TQBIBScanner.GetProcessAliases: boolean;
begin
  Result := True;
end;

function TQBIBScanner.GetQuoteIdentifiers: boolean;
begin
  Result := FSQLDialect <> qibDialect1;
end;
(*
function TQBIBScanner.IsAlpha(Ch: aqbChar): boolean;
begin
  Result := Ch in ['A'..'Z'];
end;
*)
function TQBIBScanner.IsIdentQuote(Ch: aqbChar): boolean;
begin
  case FSQLDialect of
    qibDialect1: Result := False;
    qibDialect3: Result := Ch = '"';
  else
    Result := inherited IsIdentQuote(Ch);
  end;
end;

function TQBIBScanner.IsLegalSymbol(Ch: aqbChar): boolean;
begin
  Result := inherited IsLegalSymbol(Ch) or (Ch = '$');
end;

function TQBIBScanner.IsQuote(Ch: aqbChar): boolean;
begin
  case FSQLDialect of
    qibDialect1: Result := (Ch = '''') or (Ch = '"');
    qibDialect3: Result := Ch = '''';
  else
    Result := inherited IsQuote(Ch);
  end;
end;

function TQBIBScanner.NeedQuotes(const Ident: aqbString): boolean;
var
  i: integer;
  ui: aqbString;
begin
// Modified by nnk 13.11.2010 13:58:20
// CR#27696
// Now condition is like ibmeta one
  if (Length(Ident) > 1) and IsIdentQuote(Ident[1]) and
    IsIdentQuote(Ident[Length(Ident)]) then
    Result := false
  else
  begin
    ui := UpperCase(Ident);
    Result := IsKeyword(ui) or IsFunction(ui);
    for i := 1 to Length(Ident) do
    begin
      if not (Ident[i] in ['A'..'Z', '0'..'9', '_', '$']) then
      begin
        Result := true;
        break;
      end;
    end;
    if not Result and inherited NeedQuotes(Ident) then
      Result := not IsIdentifier(Ident);
  end;
end;

function TQBIBScanner.ProcessIdent(const S: aqbString): aqbString;
begin
  Result := inherited ProcessIdent(S);
  Result := QBUpperCase(S);
end;

function TQBIBScanner.ProcessName(const S: aqbString): aqbString;
var
  I: integer;
begin
  if FSQLDialect = qibDialect1 then
  begin
    Result := QBUpperCase(S);
    for I := 1 to Length(Result) do
      if Result[I] = ' ' then
        Result[I] := '_';
  end
  else
    Result := inherited ProcessName(S);
end;

function TQBIBScanner.QuoteName(const S: aqbString): aqbString;
begin
  if FSQLDialect = qibDialect1 then
    Result := ProcessName(S)
  else
    Result := inherited QuoteName(S);
end;

{ TQBIBSelectParser }

procedure TQBIBSelectParser.Clear;
begin
  inherited Clear();
  FIBOptions.Clear();
  FFBOptions.Clear();
end;

constructor TQBIBSelectParser.Create(AParent: TPersistent);
begin
  inherited Create(AParent);
  FIBOptions := TQBIBOptions.Create();
  FFBOptions := TQBFBOptions.Create();
end;

function TQBIBSelectParser.CreateScanner: TQBSQLScanner;
begin
  Result := TQBIBScanner.Create();
end;

procedure TQBIBSelectParser.DefineOperators;
var
  Operator: TQBOperator;
begin
  inherited DefineOperators();
  Operator := Operators.Add();
  Operator.Name := 'CONTAINING';
  Operator.Caption := 'CONTAINING';

  Operator := Operators.Add();
  Operator.Name := 'STARTING WITH';
  Operator.Caption := 'STARTING WITH';

  Operator := Operators.Add;
  Operator.Name := 'NOT STARTING WITH';
  Operator.Caption := 'NOT STARTING WITH';
end;

destructor TQBIBSelectParser.Destroy;
begin
  FFBOptions.Free();
  FIBOptions.Free();
  inherited Destroy();
end;

procedure TQBIBSelectParser.DoParseAppendix;
const
  SPlan = 'PLAN';
begin
  if (FSQLServer in [qssDontCare, qssInterBase]) and
    (CompareText(Scanner.Token, 'ROWS') = 0) then
  begin
    Scanner.GetNextToken();
    if tkInteger in Scanner.TokenKind then
    begin
      FIBOptions.RowsFrom := StrToInt(Scanner.Token);
      Scanner.GetNextToken();
      if CompareText(Scanner.Token, 'TO') = 0 then
      begin
        Scanner.GetNextToken();
        if tkInteger in Scanner.TokenKind then
        begin
          FIBOptions.RowsTo := StrToInt(Scanner.Token);
          Scanner.GetNextToken();
        end
        else
        begin
          State := ssError;
          Exit;
        end;
      end;
      if CompareText(Scanner.Token, 'BY') = 0 then
      begin
        Scanner.GetNextToken();
        if tkInteger in Scanner.TokenKind then
        begin
          FIBOptions.RowsBy := StrToInt(Scanner.Token);
          Scanner.GetNextToken();
        end
        else
        begin
          State := ssError;
          Exit;
        end;
      end;
      if CompareText(Scanner.Token, 'PERCENT') = 0 then
      begin
        Scanner.GetNextToken();
        FIBOptions.Percent := True;
      end;
      if CompareText(Scanner.Token, 'WITH') = 0 then
      begin
        Scanner.GetNextToken();
        if CompareText(Scanner.Token, 'TIES') = 0 then
        begin
          Scanner.GetNextToken();
          FIBOptions.Ties := True;
        end
        else
        begin
          State := ssError;
          Exit;
        end;
      end;
    end
    else
    begin
      State := ssError;
      Exit;
    end;
  end
  else
    inherited DoParseAppendix;
end;

procedure TQBIBSelectParser.DoParsePlanClause;
  procedure GoNextNotSpaceToken;
  begin
    while (tkSpace in Scanner.TokenKind) or (tkEol in Scanner.TokenKind) do
      Scanner.GetNextToken;
  end;
begin
  Scanner.GetNextToken;
  GoNextNotSpaceToken;
  if CompareText(Scanner.Token, '(') = 0 then
  begin
    while (not (tkEnd in Scanner.TokenKind)) and
      (CompareText(Scanner.Token, ')') <> 0) and (State <> ssError) do
    begin
      Scanner.GetNextToken;
      GoNextNotSpaceToken;
      if tkIdent in Scanner.TokenKind then
      begin
        IBOptions.PlanTableName := Scanner.Token;
        Scanner.GetNextToken;
        GoNextNotSpaceToken;
        if (tkKeyword in Scanner.TokenKind) or (tkIdent in Scanner.TokenKind) then
        begin
          IBOptions.PlanName := Scanner.Token;
          Scanner.GetNextToken;
          GoNextNotSpaceToken;
          if CompareText(Scanner.Token, ')') = 0 then
          begin
            Scanner.GetNextToken;
            Break;
          end
          else
            State := ssError;
        end
        else
          State := ssError;
      end
      else
        State := ssError;
    end;
  end
  else
    State := ssError;
end;

procedure TQBIBSelectParser.DoParseSelectClause;
begin
  if SQLServer in [qssDontCare, qssFireBird] then
  begin
    if CompareText(Scanner.Token, 'FIRST') = 0 then
    begin
      Scanner.GetNextToken();
      if tkInteger in Scanner.TokenKind then
        FFBOptions.First := StrToInt(Scanner.Token)
      else
      begin
        State := ssError;
        Exit;
      end;
      Scanner.GetNextToken();
    end;
    if CompareText(Scanner.Token, 'SKIP') = 0 then
    begin
      Scanner.GetNextToken();
      if tkInteger in Scanner.TokenKind then
        FFBOptions.Skip := StrToInt(Scanner.Token)
      else
      begin
        State := ssError;
        Exit;
      end;
      Scanner.GetNextToken();
    end;
  end;
  inherited DoParseSelectClause();
end;

function TQBIBSelectParser.GetCaseSensitiveIdentifiers: boolean;
begin
  Result := not (Scanner is TQBIBScanner) or
    (TQBIBScanner(Scanner).SQLDialect = qibDialect3);
end;

function TQBIBSelectParser.GetExpression: aqbString;
begin
  Result := TableDefs.Script;
  if Result <> '' then
  begin
    case TypeQuery of
      tqSelect:
        begin
          if Distinct then
            Result := ProcessKeyword('DISTINCT') + ' ' + Result;
          if FSQLServer in [qssDontCare, qssFireBird] then
            Result := ProcessKeyword('SELECT') + ' ' + FFBOptions.Script + Result
          else
            Result := ProcessKeyword('SELECT') + ' ' + Result;
          Result := Result + WhereItems.Script + GroupByItems.Script +
            HavingItems.Script + FIBOptions.PlanScript + OrderByItems.Script;
          if FSQLServer in [qssDontCare, qssInterBase] then
            Result := Result + FIBOptions.Script;
        end;
      tqInsert: Result := ProcessKeyword('INSERT') + ' ' + ProcessKeyword('INTO') +
        Result + ValuesItems.Script;
      tqUpdate:
        begin
          Result := ProcessKeyword('UPDATE') + Result + WhereItems.Script;
          if FSQLServer in [qssDontCare, qssInterBase] then
            Result := Result + FIBOptions.Script;
        end;
      tqDelete:
        begin
          Result := ProcessKeyword('DELETE') + Result + WhereItems.Script;
          if FSQLServer in [qssDontCare, qssInterBase] then
            Result := Result + FIBOptions.Script;
        end;
    end;
    {
    if Distinct
      then Result := ProcessKeyword('DISTINCT') + ' ' + Result;
    if FSQLServer in [qssDontCare, qssFireBird]
      then Result := ProcessKeyword('SELECT') + ' ' + FFBOptions.Script + Result
      else Result := ProcessKeyword('SELECT') + ' ' + Result;
    Result := Result + WhereItems.Script + GroupByItems.Script +
      HavingItems.Script + OrderByItems.Script;
    if FSQLServer in [qssDontCare, qssInterBase]
      then Result := Result + FIBOptions.Script;
    }
  end;
end;

function TQBIBSelectParser.GetParamAsString(AFieldType: TFieldType; AValue: Variant): aqbString;

  function GetDateAsStr(AValue: Variant): aqbString;
  var
    idx: integer;
  begin
    // After load diagram from file, VarType(AValue) = varString;
    if (VarType(AValue) <> varString) then
      Result := FormatDateTime('mm/dd/yyyy', AValue)
    else
    begin
      Result := AValue;
      // cut date-part from string
      idx := Pos(' ', Result);
      if (idx > 0) then
        Result := Copy(Result, 1, idx-1);
    end;

    if {$IFDEF VCL17}FormatSettings.{$ENDIF}DateSeparator <> '/' then
     {$IFDEF VCL3}
      Result := ReplaceStr(Result, DateSeparator, '/');
     {$ELSE}
      Result := QBStringReplace(Result, {$IFDEF VCL17}FormatSettings.{$ENDIF}DateSeparator, '/', [rfReplaceAll]);
     {$ENDIF}
  end;

  function GetTimeAsStr(AValue: Variant): aqbString;
  var
    idx: integer;
  begin
    // After load diagram from file, VarType(AValue) = varString;
    if (VarType(AValue) <> varString) then
      Result := FormatDateTime('hh:nn:ss', AValue)
    else
    begin
      Result := AValue;
      // cut time-part from string
      idx := Pos(' ', Result);
      if (idx > 0) then
        Result := Copy(Result, idx+1, Length(Result)-idx);
    end;

    if {$IFDEF VCL17}FormatSettings.{$ENDIF}TimeSeparator <> ':' then
     {$IFDEF VCL3}
      Result := ReplaceStr(Result, DateSeparator, ':');
     {$ELSE}
      Result := QBStringReplace(Result, {$IFDEF VCL17}FormatSettings.{$ENDIF}DateSeparator, ':', [rfReplaceAll]);
     {$ENDIF}
  end;

begin
  case AFieldType of
    ftDate:
      Result := GetDateAsStr(AValue);
    ftTime:
      Result := GetTimeAsStr(AValue);
    ftDateTime{$IFDEF VCL6}, ftTimeStamp{$ENDIF}:
      Result := GetDateAsStr(AValue) + ' ' + GetTimeAsStr(AValue);
  end;
  Result := Scanner.QuoteStr(Result);
end;

function TQBIBSelectParser.IsProcEmptyInputParamsBracket: boolean;
begin
  Result := false;
end;

function TQBIBSelectParser.NeedParentheses: boolean;
begin
  Result := false;
end;

procedure TQBIBSelectParser.SetFBOptions(const Value: TQBFBOptions);
begin
  if FFBOptions <> Value then
    FFBOptions.Assign(Value);
end;

procedure TQBIBSelectParser.SetIBOptions(const Value: TQBIBOptions);
begin
  if FIBOptions <> Value then
    FIBOptions.Assign(Value);
end;

{ TQBFBOptions }

procedure TQBFBOptions.Assign(Source: TPersistent);
begin
  if Source is TQBFBOptions then
  begin
    FFirst := TQBFBOptions(Source).First;
    FSkip := TQBFBOptions(Source).Skip;
  end
  else
    inherited Assign(Source);
end;

procedure TQBFBOptions.Clear;
begin
  FFirst := 0;
  FSkip := 0;
end;

function TQBFBOptions.GetScript: aqbString;
const
  sFirst = 'FIRST %d';
  sSkip = 'SKIP %d';
begin
  Result := '';
  if FFirst > 0 then
    Result := QBFormat(sFirst, [FFirst]);
  if FSkip > 0 then
  begin
    if Result <> '' then
      Result := Result + ' ';
    Result := Result + QBFormat(sSkip, [FSkip]);
  end;
  if Result <> '' then
    Result := Result + ' ';
end;

{ TQBIBOptions }

procedure TQBIBOptions.Assign(Source: TPersistent);
begin
  if Source is TQBIBOptions then
  begin
    FPercent := TQBIBOptions(Source).Percent;
    FTies := TQBIBOptions(Source).Ties;
    FRowsFrom := TQBIBOptions(Source).RowsFrom;
    FRowsBy := TQBIBOptions(Source).RowsBy;
    FRowsTo := TQBIBOptions(Source).RowsTo;
    FPlanTableName := TQBIBOptions(Source).PlanTableName;
    FPlanName := TQBIBOptions(Source).PlanName;
  end
  else
    inherited Assign(Source);
end;

procedure TQBIBOptions.Clear;
begin
  FPercent := False;
  FTies := False;
  FRowsFrom := 0;
  FRowsBy := 0;
  FRowsTo := 0;
  FPlanTableName := '';
  FPlanName := '';
end;

function TQBIBOptions.GetPlanScript: aqbString;
const
  SPlanSqript: aqbString = 'PLAN'#13#10'  (%s %s)'#13#10;
begin
  if (Trim(FPlanTableName + FPlanName) <> '') then
    Result := QBFormat(SPlanSqript, [FPlanTableName, FPlanName])
  else
    Result := '';
end;

function TQBIBOptions.GetScript: aqbString;
const
  sRows = 'ROWS %d';
  sTo = 'TO %d';
  sBy = 'BY %d';
  sPercent = 'PERCENT';
  sTies = 'WITH TIES';
begin
  Result := '';
  if (FRowsFrom > 0) or (FRowsTo > 0) or (FRowsBy > 0) then
  begin
    Result := QBFormat(sRows, [FRowsFrom]);
    if FRowsTo > 0 then
      Result := Result + ' ' + QBFormat(sTo, [FRowsTo]);
    if FRowsBy > 0 then
      Result := Result + ' ' + QBFormat(sBy, [FRowsBy]);
    if FPercent then
      Result := Result + ' ' + sPercent;
    if FTies then
      Result := Result + ' ' + sTies;
  end;
end;

{ TQBIBOptionsPanel }

constructor TQBIBOptionsPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FRowsFromEdit := TEdit.Create(Self);
  FRowsFromEdit.OnChange := HandleRowsFromEditChange;
  FRowsToEdit := TEdit.Create(Self);
  FRowsToEdit.OnChange := HandleRowsToEditChange;
  FRowsByEdit := TEdit.Create(Self);
  FRowsByEdit.OnChange := HandleRowsByEditChange;
  FPercentBox := TCheckBox.Create(Self);
  FPercentBox.OnClick := HandlePercentBoxClick;
  FTiesBox := TCheckBox.Create(Self);
  FTiesBox.OnClick := HandleTiesBoxClick;
end;

procedure TQBIBOptionsPanel.HandlePercentBoxClick(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.IBOptions.Percent := FPercentBox.Checked;
end;

procedure TQBIBOptionsPanel.HandleRowsByEditChange(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.IBOptions.RowsBy := StrToIntDef(FRowsByEdit.Text, 0);
end;

procedure TQBIBOptionsPanel.HandleRowsFromEditChange(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.IBOptions.RowsFrom := StrToIntDef(FRowsFromEdit.Text, 0);
end;

procedure TQBIBOptionsPanel.HandleRowsToEditChange(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.IBOptions.RowsTo := StrToIntDef(FRowsToEdit.Text, 0);
end;

procedure TQBIBOptionsPanel.HandleTiesBoxClick(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.IBOptions.Ties := FTiesBox.Checked;
end;

{ TQBFBOptionsPanel }

constructor TQBFBOptionsPanel.Create(AOwner: TComponent);
var
  ALabel: TLabel;
begin
  inherited Create(AOwner);
  ALabel := TLabel.Create(Self);
  ALabel.Left := 8;
  ALabel.Top := 8;
  ALabel.Caption := rsWindowFirst;
  FFirstEdit := TEdit.Create(Self);
  FFirstEdit.Left := 30;
  FFirstEdit.Top := 6;
  FFirstEdit.OnChange := HandleFirstEditChange;
  ALabel := TLabel.Create(Self);
  ALabel.Left := 8;
  ALabel.Top := 32;
  ALabel.Caption := rsWindowSkip;
  FSkipEdit := TEdit.Create(Self);
  FSkipEdit.OnChange := HandleSkipEditChange;
  FSkipEdit.Left := 30;
  FSkipEdit.Top := 30;
end;

procedure TQBFBOptionsPanel.HandleFirstEditChange(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.FBOptions.First := StrToIntDef(FFirstEdit.Text, 0);
end;

procedure TQBFBOptionsPanel.HandleSkipEditChange(Sender: TObject);
begin
  if Assigned(Parser) then
    Parser.FBOptions.Skip := StrToIntDef(FSkipEdit.Text, 0);
end;

{ TIBQueryBuilder }

procedure TIBQueryBuilder.GetAvailableFields(Fields: TaqbStrings);
var
  I, J: integer;
  Table: TQBTable;
  procedure AddField(const FldName: aqbString);
  begin
    if Fields.IndexOf(FldName) = -1 then
      Fields.Add(FldName);
  end;
begin
  if not Assigned(Fields) then
    Exit;

  Fields.Clear;
  for I := 0 to Parser.TableDefs.Count - 1 do
  begin
    Table := Tables.FindByAlias(Parser.TableDefs[I].Alias);
    if (Table = nil) and Parser.IsOrderByComputedField then
    begin
      for J := 0 to Parser.TableDefs[I].FieldDefs.Count - 1 do
        if (Trim(Parser.TableDefs[I].FieldDefs[J].Name) <> '') and
          (Trim(Parser.TableDefs[I].FieldDefs[J].Name) <> '*') and
          (Trim(Parser.TableDefs[I].FieldDefs[J].Alias) <> '') and
          (Parser.TableDefs[I].FieldDefs[J].Scope in [ptOutput, ptInputOutput])
            then
          AddField(Parser.TableDefs[I].Alias + '.' +
            Parser.TableDefs[I].FieldDefs[J].Name);
    end
    else if (Table <> nil) and not Table.Destroying then
    begin
      for J := 0 to Parser.TableDefs[I].FieldDefs.Count - 1 do
      begin
        if (Trim(Parser.TableDefs[I].FieldDefs[J].Name) = '') or
          (Trim(Parser.TableDefs[I].FieldDefs[J].Name) = '*') or
          not (Parser.TableDefs[I].FieldDefs[J].Scope in
          [ptOutput, ptInputOutput]) then
          Continue;

          AddField(Parser.TableDefs[I].Alias + '.' +
             Parser.TableDefs[I].FieldDefs[J].Name);
      end;

      if AllFieldsSorted then
        for J := 0 to Table.Fields.Count - 1 do
          if not TQBListItem(Table.Fields[J]).Checked and
            (TQBListItem(Table.Fields[J]).ScopeType in
            [ptOutput, ptInputOutput]) then
            AddField(Table.Alias + '.' + TQBListItem(Table.Fields[J]).Caption);
    end;
  end;
end;

procedure TIBQueryBuilder.HandleChangeFieldAlias(const FieldName,
  Alias: aqbString; var NewAlias: aqbString);
var I: Integer;
begin
  inherited;
  I := Parser.OrderByItems.IndexOf(NewAlias);
  if I <> -1 then
    Parser.OrderByItems[I] := FieldName;
end;

end.
